/*
 * BasicIO.java - ScriptME
 * 
 * Copyright (c) 2009 Cesar Henriques <cesar at alttab.com.ar>.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Based on FESI Project
 * 
 * Contributors:
 * 	Jean-Marc Lugrin - initial API and implementation
 * 	Cesar Henriques <cesar at alttab.com.ar> - J2ME Porting and Extensions
 */
package org.scriptme.ecmascript.ext;

import org.scriptme.ecmascript.data.BuiltinFunctionObject;
import org.scriptme.ecmascript.data.ESBoolean;
import org.scriptme.ecmascript.data.ESObject;
import org.scriptme.ecmascript.data.ESString;
import org.scriptme.ecmascript.data.ESUndefined;
import org.scriptme.ecmascript.data.ESValue;
import org.scriptme.ecmascript.data.ESNull;
import org.scriptme.ecmascript.data.FunctionPrototype;
import org.scriptme.ecmascript.data.GlobalObject;
import org.scriptme.ecmascript.data.ObjectObject;
import org.scriptme.ecmascript.exceptions.EcmaScriptException;
import org.scriptme.ecmascript.exceptions.EcmaScriptParseException;
import org.scriptme.ecmascript.gui.GuiFactoryImpl;
import org.scriptme.ecmascript.interpreter.Evaluator;
import org.scriptme.ecmascript.gui.ConfirmationBox;
import org.scriptme.ecmascript.gui.MessageBox;
import org.scriptme.ecmascript.gui.PromptBox;

import java.util.Hashtable;

/**
 * Midp based basic IO for ScriptME.
 */
public class BasicIO extends Extension implements BasicIOInterface {

	/**
	 * The Class GlobalObjectWrite.
	 */
	class GlobalObjectWrite extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object write.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectWrite(String name, Evaluator evaluator, FunctionPrototype fp) {
			super(fp, evaluator, name, 1);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {
			for (int i = 0; i < arguments.length; i++) {
				System.out.print(arguments[i].toString());
			}
			return ESUndefined.theUndefined;
		}
	}

	/**
	 * The Class GlobalObjectWriteln.
	 */
	class GlobalObjectWriteln extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object writeln.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectWriteln(String name, Evaluator evaluator,
				FunctionPrototype fp) {
			super(fp, evaluator, name, 1);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {
			for (int i = 0; i < arguments.length; i++) {
				System.out.print(arguments[i].toString());
			}
			System.out.println();
			return ESUndefined.theUndefined;
		}
	}

	/**
	 * The Class GlobalObjectAlert.
	 */
	class GlobalObjectAlert extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object alert.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectAlert(String name, Evaluator evaluator, FunctionPrototype fp) {
			super(fp, evaluator, name, 1);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {

			if (arguments.length == 2) {
				MessageBox mb = GuiFactoryImpl.getInstance().displayMessageBox(
						arguments[0].toString(), arguments[1].toString());
				mb.waitOK();
			} else if (arguments.length == 1) {
				MessageBox mb = GuiFactoryImpl.getInstance().displayMessageBox(
						"EcmaScript Alert", arguments[0].toString());
				mb.waitOK();
			}

			return ESUndefined.theUndefined;
		}
	}

	/**
	 * The Class GlobalObjectPrompt.
	 */
	class GlobalObjectPrompt extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object prompt.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectPrompt(String name, Evaluator evaluator,
				FunctionPrototype fp) {
			super(fp, evaluator, name, 1);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {
			String prompt = "";
			String defaultResponse = "";
			if (arguments.length > 0)
				prompt = arguments[0].toString();
			if (arguments.length > 1)
				defaultResponse = arguments[1].toString();
			PromptBox pb = GuiFactoryImpl.getInstance().prompt(
					"EcmaScript Promt", prompt, defaultResponse);
			return new ESString(pb.waitResponse());
		}
	}

	/**
	 * The Class GlobalObjectConfirm.
	 */
	class GlobalObjectConfirm extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object confirm.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectConfirm(String name, Evaluator evaluator,
				FunctionPrototype fp) {
			super(fp, evaluator, name, 1);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {
			StringBuffer sb = new StringBuffer();
			for (int i = 0; i < arguments.length; i++) {
				sb.append(arguments[i].toString());
			}
			ConfirmationBox mb = GuiFactoryImpl.getInstance().confirm(
					"EcmaScript Confirm", sb.toString());
			boolean response = mb.waitYesOrNo();
			return ESBoolean.makeBoolean(response);
		}
	}

	/**
	 * The Class GlobalObjectExit.
	 */
	class GlobalObjectExit extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object exit.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectExit(String name, Evaluator evaluator, FunctionPrototype fp) {
			super(fp, evaluator, name, 1);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {
			int status = 0;
			if (arguments.length > 0) {
				status = arguments[0].toInt32();
			}

			System.exit(status);
			return null; // Never reached
		}
	}

	/**
	 * The Class GlobalObjectNoop.
	 */
	class GlobalObjectNoop extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object noop.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectNoop(String name, Evaluator evaluator, FunctionPrototype fp) {
			super(fp, evaluator, name, 0); // 0 = Just some default value
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {

			return ESUndefined.theUndefined;
		}
	}

	/**
	 * The Class GlobalObjectLoad.
	 */
	class GlobalObjectLoad extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object load.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectLoad(String name, Evaluator evaluator, FunctionPrototype fp) {
			super(fp, evaluator, name, 1); // 0 = Just some default value
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {
			String fileName = null;
			if (arguments.length > 0)
				fileName = arguments[0].toString();
			if (fileName == null)
				throw new EcmaScriptException("Missing file name for load");
			ESValue value;
			try {
				value = this.evaluator.evaluateLoadModule(fileName);
			} catch (EcmaScriptParseException e) {
				e.setNeverIncomplete();
				throw e;
			}
			return value;
		}
	}

	/**
	 * The Class GlobalObjectSetProperty.
	 */
	class GlobalObjectSetProperty extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object set property.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectSetProperty(String name, Evaluator evaluator,
				FunctionPrototype fp) {
			super(fp, evaluator, name, 1); // 0 = Just some default value
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {

			if (arguments.length < 2)
				return ESNull.theNull;

			ESValue retVal = (ESValue) BasicIO._globalProperties.put(
					arguments[0], arguments[1]);

			if (retVal == null)
				return ESNull.theNull;
			else
				return retVal;
		}
	}

	/**
	 * The Class GlobalObjectGetProperty.
	 */
	class GlobalObjectGetProperty extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object get property.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectGetProperty(String name, Evaluator evaluator,
				FunctionPrototype fp) {
			super(fp, evaluator, name, 1); // 0 = Just some default value
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {

			if (arguments.length < 1)
				return ESNull.theNull;

			ESValue retVal = (ESValue) BasicIO._globalProperties
					.get(arguments[0]);

			if (retVal == null)
				return ESNull.theNull;
			else
				return retVal;
		}
	}

	/**
	 * The Class GlobalObjectRemoveProperty.
	 */
	class GlobalObjectRemoveProperty extends BuiltinFunctionObject {

		/**
		 * Instantiates a new global object remove property.
		 * 
		 * @param name
		 *            the name
		 * @param evaluator
		 *            the evaluator
		 * @param fp
		 *            the fp
		 */
		GlobalObjectRemoveProperty(String name, Evaluator evaluator,
				FunctionPrototype fp) {
			super(fp, evaluator, name, 1); // 0 = Just some default value
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see org.scriptme.ecmascript.data.FunctionPrototype#callFunction(org.scriptme.ecmascript.data.ESObject,
		 *      org.scriptme.ecmascript.data.ESValue[])
		 */
		public ESValue callFunction(ESObject thisObject, ESValue[] arguments)
				throws EcmaScriptException {

			if (arguments.length < 1)
				return ESNull.theNull;

			ESValue retVal = (ESValue) BasicIO._globalProperties
					.remove(arguments[0]);

			if (retVal == null)
				return ESNull.theNull;
			else
				return retVal;
		}
	}

	/** The _global properties. */
	private static Hashtable _globalProperties = new Hashtable();

	/** The evaluator. */
	private Evaluator evaluator = null;

	/** The document. */
	private ESObject document = null;

	/** The window. */
	private ESObject window = null;

	/**
	 * Instantiates a new basic io.
	 */
	public BasicIO() {
		super();
	}

	// implements BasicIOInterface
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ext.BasicIOInterface#getDocument()
	 */
	public ESObject getDocument() {
		return document;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.scriptme.ecmascript.ext.Extension#initializeExtension(org.scriptme.ecmascript.interpreter.Evaluator)
	 */
	public void initializeExtension(Evaluator evaluator)
			throws EcmaScriptException {

		this.evaluator = evaluator;

		GlobalObject go = evaluator.getGlobalObject();
		document = ObjectObject.createObject(evaluator);
		window = ObjectObject.createObject(evaluator);
		FunctionPrototype fp = (FunctionPrototype) evaluator
				.getFunctionPrototype();

		go.putHiddenProperty("document", document);
		document.putHiddenProperty("write", new GlobalObjectWrite("write",
				evaluator, fp));
		document.putHiddenProperty("writeln", new GlobalObjectWriteln(
				"writeln", evaluator, fp));
		document.putHiddenProperty("open", new GlobalObjectNoop("open",
				evaluator, fp));
		document.putHiddenProperty("close", new GlobalObjectNoop("close",
				evaluator, fp));
		document.putHiddenProperty("URL", new ESString("file://<unknown>"));

		go.putHiddenProperty("window", window);
		window.putHiddenProperty("alert", new GlobalObjectAlert("alert",
				evaluator, fp));
		window.putHiddenProperty("prompt", new GlobalObjectPrompt("prompt",
				evaluator, fp));
		window.putHiddenProperty("confirm", new GlobalObjectConfirm("confirm",
				evaluator, fp));

		go.putHiddenProperty("write", new GlobalObjectWrite("write", evaluator,
				fp));
		go.putHiddenProperty("writeln", new GlobalObjectWriteln("writeln",
				evaluator, fp));
		go.putHiddenProperty("alert", new GlobalObjectAlert("alert", evaluator,
				fp));
		go.putHiddenProperty("prompt", new GlobalObjectPrompt("prompt",
				evaluator, fp));
		go.putHiddenProperty("confirm", new GlobalObjectConfirm("confirm",
				evaluator, fp));
		go.putHiddenProperty("exit",
				new GlobalObjectExit("exit", evaluator, fp));
		go.putHiddenProperty("load",
				new GlobalObjectLoad("load", evaluator, fp));

		go.putHiddenProperty("setProperty", new GlobalObjectSetProperty(
				"setProperty", evaluator, fp));
		go.putHiddenProperty("getProperty", new GlobalObjectGetProperty(
				"getProperty", evaluator, fp));
		go.putHiddenProperty("removeProperty", new GlobalObjectRemoveProperty(
				"removeProperty", evaluator, fp));
	}
}
